import types
import random
#import visual_system

class Settings(dict):
    def __init__(self):
        self.listeners = []

    def __getattr__(self, key):
        '''
        This allows for game_settings.foo, as well as game_settings["foo"]
        '''
        self.key = None
        return self.key

    def set_command(self, string):
        print(self.player, self.active_player)
        if self.player == self.active_player or string.startswith("q"):
            self.next_command["value"] = string
            self.next_command_ready = True
        else:
            self.visual_system.set_error("It's not your turn to play yet.")
            self.next_command_ready = False

    def handle_event(self, event):
        # The listener will fill out this table if a proper event has been sent
        for listener in self.listeners:
            result = listener.handle_event(event, self)
            if result:
                return result


GameSettings = Settings()
gs = GameSettings
gs.next_command = {}

class Deck(object):
    def __init__(self):
        self.cards = []
        self.discarded_cards = []

    def add_card(self, card):
        assert isinstance(card, Card), "add_card must have a card as its first argument."
        self.cards.append(card)
    
    def add_discarded_card(self, card):
        self.discarded_cards.append(card)
    
    def shuffle(self):
        random.shuffle(self.cards)

    def draw_card(self):
        assert len(self.cards) > 0 or len(self.discarded_cards) > 0, "There are no cards left to deal!"
        if len(self.cards) == 0:
            self.cards = self.discarded_cards
            self.shuffle()
            self.discarded_cards = []
        card = self.cards[0]
        self.cards.remove(card)
        return card

Deck.global_deck = Deck()

class Player(object):
    def __init__(self, name = "Undefined"):
        self.name = name
        self.deck = gs.deck
        self.cards = []
        self.characters = []
        self.drawing_limit = gs.initial_drawing_limit

        gs.listeners.append(self)
        
        # visual logic
        self.pos = (-9001,-9001)

    def handle_event(self, event, gs = {}):
        #TODO: break / continue
        for character in self.characters:
            character.handle_event(event, gs)
        for card in self.cards:
            card.handle_event(event, gs)
        
    def __str__(self):
        return "Player called %s, holding %s cards" % (self.name, len(self.cards))

    def draw_card(self):
        '''
        Draws a card from the deck.
        '''
        return self.take_card(self.deck.draw_card())

    def take_card(self, card):
        assert not card in self.cards, "There are no repeated cards in Gloom."
        assert isinstance(card, Card), "take_card must have a card as its first argument."
        assert not isinstance(card, Character), "character cards should never be in a player's hand"
        self.cards.append(card)
        card.set_player(self)
        return card

    def discard_card(self, card_idx = None, card = None):
        card = card or self.cards[card_idx]
        
        card.set_player(None)
        self.cards.remove(card)
        self.deck.add_discarded_card(card)
    
    def place_card(self, card, target_card, target_player = None):
        target_player = target_player or self
        assert card in self.cards, "Can't play a card you don't have in your hand."
        assert isinstance(target_card, Card), "Modifiers may only be placed on a character card."
        assert isinstance(target_card, Character), "Modifiers may only be placed on a character card."

        if isinstance(card, UntimelyDeath):
            target_card.calculate_modifiers()
            assert sum(target_card.modifiers) < 0, "Can't kill a character with a non-negative self-worth"
            assert gs.moves_made == 0, "Can only kill a character on the first move of a turn."
            
        if card.effect:
            card.effect(target_player, target_card)
        target_card.place_card(card)
        self.discard_card(card = card)

    def add_character(self, char):
        self.characters.append(char)
        char.player = self

    def draw_until_limit(self):
        while len(self.cards) < self.drawing_limit:
            self.draw_card()

class Card(object):
    num_modifiers = 3
    names_already_used = set()

    def __init__(self, type = "Undefined", name = "Undefined"):
        assert not name in Card.names_already_used, "There are no repeated cards in Gloom."
        Card.names_already_used.add(name)
        self.set_type(type)
        self.name = name
        self.player = None

        #self.card_surface = gs.visual_system.create_card(self)
    
    def set_type(self, type):
        self.type = type

    def __str__(self):
        return "<Card: %s, %s>" % (self.type, self.get_name())

    def get_name(self):
        return self.name

    def set_player(self, player):
        assert (not player) or isinstance(player, Player), "set_player must have a player as its first argument."
        self.player = player
    
    def on_click(self):
        print self
        gs.selected_card = self

class Character(Card):
    def __init__(self, name):
        self.modifiers = [0] * Card.num_modifiers
        self.cards = []
        Card.__init__(self, "character", name)
        
    def is_dead(self):
        return self.cards and len(self.cards) > 0 and isinstance(self.cards[-1], UntimelyDeath)

    def on_click(self):
        Card.on_click(self)
        gs.next_command["target_card"] = self
        gs.next_command["target_player"] = self.player

    def place_card(self, card):
        assert not self.is_dead(), "Can't place modifiers on a dead character."
        self.cards.append(card)
        self.calculate_modifiers()

    def remove_top_card(self):
        self.cards.remove(self.cards[-1])
        self.calculate_modifiers()

    def __str__(self):
        return Card.__str__(self) + "<%s>" % self.modifiers

    def get_name(self):
        #if self.is_dead():
            #return "The dearly departed " + self.name
        return self.name

    def calculate_modifiers(self):
        self.modifiers = [0] * Card.num_modifiers
        for card in self.cards:
            for x in range(Card.num_modifiers):
                self.modifiers[x] = card.modifiers[x] or self.modifiers[x]
        self.card_surface = gs.visual_system.create_card(self)
        return self.modifiers

class Modifier(Card):
    def __init__(self, name = None, modifiers = [], effect = None):
        '''
        This is a class that represents a modifier card.
        It takes a name, a list of three numbers or None's(the modifiers), and an optional effect(a function).
        '''
        assert len(modifiers) == Card.num_modifiers, "The list of modifiers must have at most %s elements" % Card.num_modifiers
        assert ((not effect) or type(effect) == types.FunctionType) or type(effect) == types.LambdaType, "The 'effect' parameter must be a function or None"
        self.modifiers = modifiers
        self.effect = effect
        Card.__init__(self, "modifier", name)

    def __str__(self):
        return Card.__str__(self) + "<%s, %s>" % (self.modifiers, self.effect)

    def on_click(self):
        Card.on_click(self)
        if self.player != gs.players[gs.active_player]:
            print "Only touch your cards, ok?"
            return
        gs.next_command["played_card"] = self

class UntimelyDeath(Modifier):
    def __init__(self, name = None, modifiers = [None, None, None], effect = None):
        Modifier.__init__(self, name, modifiers, effect)
        # TODO: clean this up
        Card.set_type(self, "death")


